package com.example.processors;


import cn.hutool.core.util.ObjectUtil;
import com.example.annotatedclass.ClassAnnotatedBean;
import com.example.annotations.ConvertTargetClass;
import com.example.annotations.ConvertTargetField;
import com.example.exception.ProcessingException;
import com.example.factory.FactoryGroupedClasses;
import com.google.auto.service.AutoService;



import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.*;
import javax.lang.model.util.Types;



@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.cdmone.annotations.ConvertTargetClass")
@AutoService(Processor.class)
public class GetterProcessor extends AbstractProcessor {

    private Messager messager; // 编译时期输入日志的
    private Types typeUtils;
    private Elements elementUtils;
    private Filer filer;
    private java.util.List<FactoryGroupedClasses> factoryClasses = new ArrayList<>();


    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        typeUtils = processingEnv.getTypeUtils();
        elementUtils = processingEnv.getElementUtils();
        filer = processingEnv.getFiler();
        messager = processingEnv.getMessager();
    }


    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        try {
            Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(ConvertTargetClass.class);
            // 因为我们已经知道它是ElementKind.CLASS类型，所以可以直接强制转换

            for(Element annotatedElement : elementsAnnotatedWith){
                // Check if a class has been annotated with @Factory
                if (annotatedElement.getKind() != ElementKind.CLASS) {
                    continue;
                }
                TypeElement typeElement = (TypeElement) annotatedElement;
                ClassAnnotatedBean annotatedClass = new ClassAnnotatedBean(typeElement); // throws IllegalArgumentException
                checkValidClass(annotatedClass);

                // Checks if id is conflicting with another @Factory annotated class with the same id
                factoryClasses.add(new FactoryGroupedClasses(annotatedClass));
            }
            for (FactoryGroupedClasses factoryClass : factoryClasses) {
                factoryClass.generateCode(elementUtils,filer);
                messager.printMessage(Diagnostic.Kind.NOTE, factoryClass.getClassAnnotatedBean().getTypeElement().getQualifiedName() + " has been processed");
            }
            factoryClasses.clear();//下次调用清除，不然会重复写入
        } catch (ProcessingException e) {
            error(e.getElement(), e.getMessage());
        }catch (IOException io){
            io.printStackTrace();
        }

        return true;
    }

    private void checkValidClass(ClassAnnotatedBean item) throws ProcessingException {

        // Cast to TypeElement, has more type specific methods
        TypeElement classElement = item.getTypeElement();

        if (!classElement.getModifiers().contains(Modifier.PUBLIC)) {
            throw new ProcessingException(classElement, "The class %s is not public.",
                    classElement.getQualifiedName().toString());
        }

        // Check if it's an abstract class
        if (classElement.getModifiers().contains(Modifier.ABSTRACT)) {
            throw new ProcessingException(classElement,
                    "The class %s is abstract. You can't annotate abstract classes with @%",
                    classElement.getQualifiedName().toString(), ConvertTargetClass.class.getSimpleName());
        }

        // Check inheritance: Class must be childclass as specified in @Factory.type();
        TypeElement superClassElement =
                elementUtils.getTypeElement(item.getQualifiedFactoryGroupName());
        if (superClassElement.getKind() == ElementKind.INTERFACE) {
            // Check interface implemented
            if (!classElement.getInterfaces().contains(superClassElement.asType())) {
                throw new ProcessingException(classElement,
                        "The class %s annotated with @%s must implement the interface %s",
                        classElement.getQualifiedName().toString(), ConvertTargetClass.class.getSimpleName(),
                        item.getQualifiedFactoryGroupName());
            }
        }

        // Check if an empty public constructor is given
        for (Element enclosed : classElement.getEnclosedElements()) {
            if (enclosed.getKind() == ElementKind.CONSTRUCTOR) {
                ExecutableElement constructorElement = (ExecutableElement) enclosed;
                if (constructorElement.getParameters().size() == 0 && constructorElement.getModifiers()
                        .contains(Modifier.PUBLIC)) {
                    // Found an empty constructor
                    return;
                }
            }
        }

        // No empty constructor found
        throw new ProcessingException(classElement,
                "The class %s must provide an public empty default constructor",
                classElement.getQualifiedName().toString());
    }



    /**
     * 这里必须指定，这个注解处理器是注册给哪个注解的。改方法不执行可能导致process方法不会执行
     * 注意，它的返回值是一个字符串的集合，包含本处理器想要处理的注解类型的合法全称。
     * 换句话说，在这里定义你的注解处理器注册到哪些注解上。
     *
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        types.add(ConvertTargetClass .class.getCanonicalName());
        types.add(ConvertTargetField  .class.getCanonicalName());
        return types;
    }




    private void error(Element e, String msg, Object... args) {
        messager.printMessage(
                Diagnostic.Kind.ERROR,
                String.format(msg, args),
                e);
    }


}